----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    19:24:03 03/25/2011 
-- Design Name: 
-- Module Name:    adc - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE; 
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;


--************************************************************************
--	GAIN < {B3 B2 B1 B0}{A3 A2 A1 A0}

--************************************************************************

entity ADC is
	-- ENABLE - wlaczenie ukladu
	
	-- CHANNELS_SAMPLE_ENABLE - uruchamia procedure 
	-- pobierania nastepenej probki.
	 
	-- BUSY - podniesiony w trakcie ustawiania GAIN oraz 
	-- pobierania probki. Po odebraniu probki opada
	-- (w stanach PROPAGATE_*). Podnosi sie po nastepnym
	-- podniesieniu CHANNELS_SAMPLE_ENABLE.
    Port ( CLOCK : in  STD_LOGIC;
			  GAIN_ENABLE : in  STD_LOGIC;
			  GAIN : in STD_LOGIC_VECTOR (7 downto 0);
			  CHANNELS_SAMPLE_ENABLE : in STD_LOGIC;
			  SPI_MISO : in STD_LOGIC;
			  CHANNEL_A_SAMPLE : out STD_LOGIC_VECTOR (15 downto 0);
			  CHANNEL_B_SAMPLE : out STD_LOGIC_VECTOR (15 downto 0);
			  AMP_CS : out STD_LOGIC;
			  AMP_SHDN : out STD_LOGIC;
           SPI_SCK : out  STD_LOGIC;
			  SPI_MOSI : out STD_LOGIC;
			  AD_CONV : out STD_LOGIC;
			  BUSY : out STD_LOGIC;
			  SPI_SS_B : out STD_LOGIC;
			  SF_CE0 : out STD_LOGIC;
			  DAC_CS : out STD_LOGIC;
			  FPGA_INIT_B : out STD_LOGIC);
end ADC;
  
architecture Behavioral of ADC is
	type MAIN_STATE_TYPE is (
		-- Wstepne inicjalizacja
		WAIT_GAIN_ENABLE,
		-- Obsluga GAIN.
		GAIN_INIT_LOW_SETUP, 
		GAIN_HIGH, GAIN_HIGH_HOLD, 
		GAIN_LOW, GAIN_LOW_HOLD,
		-- Podnoszenie AD_CONV oraz wysterowanie licznikow i rejestrow.
		AD_INIT_WAIT, AD_INIT, 
		-- Obsluguje dwa takty SPI_SCK 'wypelnienia' na poczatku
		-- pobierania probki, pomiedzy probka 1 i 0 oraz po probce 0.
		AD_WAIT_HIGH, AD_WAIT_HIGH_HOLD, 
		AD_WAIT_LOW, AD_WAIT_LOW_HOLD,
		-- Pobieranie skonwertowanych probke obu kanalow.
		AD_READ_HIGH, AD_READ_HIGH_HOLD, 
		AD_READ_LOW, AD_READ_LOW_HOLD,
		-- Wystawianie probek na wyjscie i czekanie na obsluge nastepnej 
		-- probki.
		PROPAGATE_SAMPLE, PROPAGATE_SAMPLE_WAIT); 
		
	signal MAIN_STATE : MAIN_STATE_TYPE := WAIT_GAIN_ENABLE;
	signal MAIN_STATE_NEXT_STATE : MAIN_STATE_TYPE := WAIT_GAIN_ENABLE;
	
	signal COUNTER_COUNT_ENABLE : STD_LOGIC := '0';
	signal COUNTER_RESET : STD_LOGIC := '0';
	signal COUNTER : STD_LOGIC_VECTOR (7 downto 0) := X"00";
	
	signal REGISTER_SHIFT_ENABLE : STD_LOGIC := '0';
	signal REGISTER_LOAD_ENABLE : STD_LOGIC := '0';
	signal REGISTER_OUTPUT : STD_LOGIC := '0';
	signal REGISTER_LOAD_DATA : STD_LOGIC_VECTOR (7 downto 0) := X"00";
	signal REGISTER_DATA : STD_LOGIC_VECTOR (7 downto 0) := X"00";
	
	signal CHANNEL_INPUT_ENABLE : STD_LOGIC := '0';
	signal CHANNEL_INPUT : STD_LOGIC := '0';
	signal CHANNEL_RESET : STD_LOGIC := '0';
	signal CHANNEL_DATA : STD_LOGIC_VECTOR (27 downto 0);
	
	signal CHANNELS_SAMPLE_LATCH_ENABLE : STD_LOGIC := '0';
	signal CHANNELS_SAMPLE_LATCH : STD_LOGIC_VECTOR (27 downto 0) := (others => '0');
	
begin
--######################################################################
--								Main state machine
--######################################################################
	process (CLOCK)
	begin
		if (rising_edge (CLOCK)) then
			MAIN_STATE <= MAIN_STATE_NEXT_STATE;
		end if;
	end process;
	
	process (MAIN_STATE,GAIN_ENABLE,COUNTER,CHANNELS_SAMPLE_ENABLE)
	begin	
		case (MAIN_STATE) is
			when WAIT_GAIN_ENABLE =>
				if (GAIN_ENABLE = '1') then
					MAIN_STATE_NEXT_STATE <= GAIN_INIT_LOW_SETUP;
				else
					MAIN_STATE_NEXT_STATE <= WAIT_GAIN_ENABLE;
				end if;
			when GAIN_INIT_LOW_SETUP =>
				MAIN_STATE_NEXT_STATE <= GAIN_LOW_HOLD;
			when GAIN_HIGH =>
				MAIN_STATE_NEXT_STATE <= GAIN_HIGH_HOLD;
			when GAIN_HIGH_HOLD =>
				MAIN_STATE_NEXT_STATE <= GAIN_LOW;
			when GAIN_LOW =>
				MAIN_STATE_NEXT_STATE <= GAIN_LOW_HOLD;
			when GAIN_LOW_HOLD =>
				if (COUNTER = X"08") then
					MAIN_STATE_NEXT_STATE <= AD_INIT_WAIT;
				else
					MAIN_STATE_NEXT_STATE <= GAIN_HIGH;
				end if;
			when AD_INIT_WAIT =>
				if (CHANNELS_SAMPLE_ENABLE = '1') then
					MAIN_STATE_NEXT_STATE <= AD_INIT;
				elsif (GAIN_ENABLE = '1') then
					MAIN_STATE_NEXT_STATE <= GAIN_INIT_LOW_SETUP;
				else
					MAIN_STATE_NEXT_STATE <= AD_INIT_WAIT;
				end if;
			when AD_INIT =>
				MAIN_STATE_NEXT_STATE <= AD_WAIT_HIGH;
			when AD_WAIT_HIGH =>
				MAIN_STATE_NEXT_STATE <= AD_WAIT_HIGH_HOLD;
			when AD_WAIT_HIGH_HOLD =>
				MAIN_STATE_NEXT_STATE <= AD_WAIT_LOW; 
			when AD_WAIT_LOW =>
				MAIN_STATE_NEXT_STATE <= AD_WAIT_LOW_HOLD;
			when AD_WAIT_LOW_HOLD =>
				-- po 2 takcie odczytaj kanal pierwszy
				-- po 18 takcie odczytaj kanal zerowy
				if (COUNTER = X"02" or COUNTER = X"12") then
					MAIN_STATE_NEXT_STATE <= AD_READ_HIGH;
				-- po 34 takcie wystaw probke na wyjscie
				elsif (COUNTER = X"22" ) then
					MAIN_STATE_NEXT_STATE <= PROPAGATE_SAMPLE;
				else
					MAIN_STATE_NEXT_STATE <= AD_WAIT_HIGH;
				end if;
			when AD_READ_HIGH =>
				MAIN_STATE_NEXT_STATE <= AD_READ_HIGH_HOLD;
			when AD_READ_HIGH_HOLD =>
				MAIN_STATE_NEXT_STATE <= AD_READ_LOW;
			when AD_READ_LOW =>
				MAIN_STATE_NEXT_STATE <= AD_READ_LOW_HOLD;
			when AD_READ_LOW_HOLD =>
				if (COUNTER = X"10" or COUNTER = X"20") then
					MAIN_STATE_NEXT_STATE <= AD_WAIT_HIGH;
				else
					MAIN_STATE_NEXT_STATE <= AD_READ_HIGH;
				end if;
			-- wystaw probke na wyjscie, tutaj opada BUSY!
			when PROPAGATE_SAMPLE =>
				MAIN_STATE_NEXT_STATE <= PROPAGATE_SAMPLE_WAIT;
			-- czekaj az wystawiona probka zostanie obsluzona i
			-- uzytkownik ponownie podniesie CHANNELS_SAMPLE_ENABLE
			-- aby odebrac nastepna probke
			when PROPAGATE_SAMPLE_WAIT =>
				if (CHANNELS_SAMPLE_ENABLE = '1') then
					MAIN_STATE_NEXT_STATE <= AD_INIT;
				elsif (GAIN_ENABLE = '1') then
					MAIN_STATE_NEXT_STATE <= GAIN_INIT_LOW_SETUP;
				else
					MAIN_STATE_NEXT_STATE <= PROPAGATE_SAMPLE_WAIT;
				end if;
		end case;
	end process;
	
	process (MAIN_STATE,GAIN,REGISTER_OUTPUT,CHANNELS_SAMPLE_LATCH,SPI_MISO)
	begin
		AMP_CS <= '1';
		AMP_SHDN <= '0';
		SPI_SCK <= '0';
		SPI_MOSI <= '0';
		AD_CONV <= '0';
		BUSY <= '1';
		SPI_SS_B <= '1';
		SF_CE0 <= '1';
		FPGA_INIT_B <= '0';
		DAC_CS <= '1';
		CHANNEL_A_SAMPLE <= CHANNELS_SAMPLE_LATCH(27) & CHANNELS_SAMPLE_LATCH(27) & CHANNELS_SAMPLE_LATCH (27 downto 14);
		CHANNEL_B_SAMPLE <= CHANNELS_SAMPLE_LATCH(13) & CHANNELS_SAMPLE_LATCH(13) & CHANNELS_SAMPLE_LATCH (13 downto 0);
		COUNTER_COUNT_ENABLE <= '0';
		COUNTER_RESET <= '0';
		REGISTER_LOAD_DATA <= X"00";
		REGISTER_SHIFT_ENABLE <= '0';
		REGISTER_LOAD_ENABLE <= '0';
		CHANNEL_INPUT_ENABLE <= '0';
		CHANNEL_INPUT <= '0';
		CHANNEL_RESET <= '0';
		CHANNELS_SAMPLE_LATCH_ENABLE <= '0';
		
		case (MAIN_STATE) is
			when WAIT_GAIN_ENABLE =>
				BUSY <= '0';
			when GAIN_INIT_LOW_SETUP =>
				AMP_CS <= '0';
				COUNTER_RESET <= '1';				
				REGISTER_LOAD_DATA <= GAIN;
				REGISTER_LOAD_ENABLE <= '1';
			when GAIN_HIGH =>
				AMP_CS <= '0';
				SPI_SCK <= '1';
				SPI_MOSI <= REGISTER_OUTPUT;
			when GAIN_HIGH_HOLD =>
				AMP_CS <= '0';
				SPI_SCK <= '1';
				SPI_MOSI <= REGISTER_OUTPUT;
			when GAIN_LOW =>
				AMP_CS <= '0';
				SPI_SCK <= '0';
				COUNTER_COUNT_ENABLE <= '1';
				REGISTER_SHIFT_ENABLE <= '1';
			when GAIN_LOW_HOLD =>
				AMP_CS <= '0';
				SPI_SCK <= '0';
				SPI_MOSI <= REGISTER_OUTPUT;
			when AD_INIT_WAIT =>
				SPI_SCK <= '0';
				BUSY <= '0';
			when AD_INIT =>
				COUNTER_RESET <= '1';
				CHANNEL_RESET <= '1';
				SPI_SCK <= '0';
				AD_CONV <= '1';			
			when AD_WAIT_HIGH =>
				COUNTER_COUNT_ENABLE <= '1';
				SPI_SCK <= '1';
			when AD_WAIT_HIGH_HOLD =>
				SPI_SCK <= '1';
			when AD_WAIT_LOW =>
				SPI_SCK <= '0';
			when AD_WAIT_LOW_HOLD =>
				SPI_SCK <= '0';
			when AD_READ_HIGH => 
				COUNTER_COUNT_ENABLE <= '1';
				SPI_SCK <= '1';
			when AD_READ_HIGH_HOLD =>
				SPI_SCK <= '1';
			when AD_READ_LOW =>
				CHANNEL_INPUT_ENABLE <= '1';
				CHANNEL_INPUT <= SPI_MISO;
				SPI_SCK <= '0';
			when AD_READ_LOW_HOLD =>
				SPI_SCK <= '0';
			when PROPAGATE_SAMPLE =>
				SPI_SCK <= '0';
				CHANNELS_SAMPLE_LATCH_ENABLE <= '1';
			when PROPAGATE_SAMPLE_WAIT =>
				SPI_SCK <= '0';
				BUSY <= '0';
		end case;
	end process;
	
--######################################################################
--								Counters
--######################################################################	
	process (CLOCK,COUNTER_RESET)
	begin
		if (COUNTER_RESET = '1') then
			COUNTER <= X"00";
		else
			if (rising_edge (CLOCK)) then
				if (COUNTER_COUNT_ENABLE = '1') then
					COUNTER <= COUNTER + 1;
				end if;
			end if;
		end if;
	end process;

--######################################################################
--								Registers
--######################################################################	
	process (CLOCK)
	begin
		if (rising_edge (CLOCK)) then
			if (REGISTER_LOAD_ENABLE = '1') then
				REGISTER_DATA <= REGISTER_LOAD_DATA;
			else
				if (REGISTER_SHIFT_ENABLE = '1') then
					REGISTER_DATA <= REGISTER_DATA (6 downto 0) & '0';
				end if;
			end if;
		end if;
	end process;
	
	process (CLOCK,CHANNEL_RESET)
	begin
		if (rising_edge (CLOCK)) then
			if (CHANNEL_RESET = '1') then
				CHANNEL_DATA <= (others => '0');
			else
				if (CHANNEL_INPUT_ENABLE = '1') then
					CHANNEL_DATA <= CHANNEL_DATA (26 downto 0) & CHANNEL_INPUT;
				end if;
			end if;
		end if;
	end process; 
	
	REGISTER_OUTPUT <= REGISTER_DATA (7);
	
--######################################################################
--								Latches
--######################################################################
process (CLOCK,CHANNELS_SAMPLE_LATCH_ENABLE,CHANNEL_DATA)
begin
	if (rising_edge (CLOCK)) then
		if (CHANNELS_SAMPLE_LATCH_ENABLE = '1') then
			CHANNELS_SAMPLE_LATCH <= CHANNEL_DATA;
		end if;
	end if;
end process;

end Behavioral;